#ifndef _COMM_HPP_ //防止头文件倍重复包含
#define _COMM_HPP_

#include<iostream>
#include<cerrno>
#include<cstring>
#include<cstdlib>//退出函数（exit)
#include<sys/ipc.h>//用于键值生成（ftok）
#include<sys/shm.h>//共享内存操作（shmget，shmat，shmdt，shmctl）


#define PATHNAME "."//ftok使用路径（当前目录）
#define PROJ_ID 0X66 //ftok使用的项目ID（必须是1字节，0x66=102）
#define MAX_SIZE 4097 //共享内存大小(故意设为非4KB整数倍，测试系统行为)
//os中存在多个共享内存通信，所以os开辟的空间会更大，但是这个共享内存只能用这么多（4097），其余多出来的用于对申请空间做管理，会有一些属性，相当于数据结构
using namespace std;


key_t getKey()//求key值
{
    key_t k=ftok(PATHNAME,PROJ_ID);//生成唯一的键值，成功key值，失败ccerr
    //cin,cout,cerr ->stdin,stdout,stderr->0,1,2 一一对应
    if(k<0)
    {
     cerr<<errno<<":"<<strerror(errno)<<endl;
     exit(1);
    }
    return k;
}
int getShmHelper(key_t key,int flags)
{
     //k是要shmget，设置进入共享内存属性中的！用来标识
     //该共享内存，在内核中的唯一性
     //shmid vs key:创建共享内存，通过唯一的key来标识唯一的共享内存。key是内核级os，用户用shmid访问。进程通过key调用shmget获得shmid，key的生命周期于osIPC资源生命周期一致，shmid的生命周期只与进程相关
     //如果只用key不方便管理，就好像身份证和学号，工号之间的关系一样。
     //系统必定与应用层标定不一样，才能在系统层更改时，不影响应用层。
     int shmid=shmget(key,MAX_SIZE,flags);
     if(shmid<0)
     {
        cerr<<errno<<":"<<strerror(errno)<<endl;
        exit(2);
     }
     return shmid;
 }
 int getShm(key_t k)//获取共享内存
 {
   return getShmHelper(k,IPC_CREAT);//存在则获取，不存在创建
 }
 int createShm(key_t k)//创建共享内存
 {
    return getShmHelper(k,IPC_CREAT|IPC_EXCL|0600);//独占创建，权限0600
    //IPC_CREAT获取（单独使用就是获取） 如果与IPC_EXCL(不能单独使用)结合为创建
 }
 void* attachShm(int shmid)//添加要一起共享内存的进程
 {void* men=shmat(shmid,nullptr,0);//附加到进程地址空间，64系统，8
  //shmid为共享内存的对象，nullptr的位置是填地址，如果为空就是自动匹配，第三个位置为附加新版各位标志位
   //shmat 返回的指针可能为 64 位地址，判断错误时需显式转换为 long long,失败返回-1
   if((long long)men==-1L)
   { std::cerr <<"shmat: "<< errno << ":" << strerror(errno) << std::endl;
     exit(3);
   }
   return men;
 }
 void detachShm(void *start)//分离共享内存
 {
    if(shmdt(start)==-1)//
    {
        std::cerr <<"shmdt: "<< errno << ":" << strerror(errno) << std::endl;
    }
 }
 void delShm(int shmid)//删除共享内存
 {
    if(shmctl(shmid,IPC_RMID,nullptr)==-1)
    {
        std::cerr << errno << " : " << strerror(errno) << std::endl;
    }
 }




#endif
